#!/usr/bin/env python3
"""
Telnet协议处理器
"""
import asyncio
import base64
import uuid
from datetime import datetime, timezone

from .base_handler import ProtocolHandler
from .utils import simulate_command_execution
from .config import get_telnet_config
from .logger import op_logger
from multihoneypot.attacklogger import attack_logger, AttackType, ProtocolType

class TelnetHandler(ProtocolHandler):
    """
    Telnet协议处理器，模拟网络设备（如路由器）并记录交互
    """

    def __init__(self, host, port, log_file, config=None):
        """
        初始化Telnet处理器

        Args:
            host (str): 监听主机地址
            port (int): 监听端口
            log_file (str): 日志文件路径
            config (configparser.ConfigParser, optional): 配置对象
        """
        super().__init__("telnet", host, port, log_file)

        # 从配置中获取Telnet设置
        if config:
            telnet_config = get_telnet_config(config)
            self.TELNET_BANNER = telnet_config.get("banner", b"\r\n\r\nWelcome to Telnet Server\r\nLogin authentication\r\n\r\n")
            self.TELNET_PROMPT = telnet_config.get("prompt", b"Router> ")
            self.TELNET_ENABLE_PROMPT = telnet_config.get("enable_prompt", b"Router# ")
            self.TELNET_LOGIN_PROMPT = telnet_config.get("login_prompt", b"Username: ")
            self.TELNET_PASS_PROMPT = telnet_config.get("password_prompt", b"Password: ")
            self.DEVICE_INFO = telnet_config.get("device_info", {
                "hostname": "Router",
                "model": "Cisco IOS Router",
                "version": "12.4(25b)",
                "hardware": "CISCO2811",
                "uptime": "3 days, 14 hours, 25 minutes",
                "interfaces": ["FastEthernet0/0", "FastEthernet0/1", "Serial0/0/0"]
            })
        else:
            # 默认配置
            self.TELNET_BANNER = b"\r\n\r\nWelcome to Telnet Server\r\nLogin authentication\r\n\r\n"
            self.TELNET_PROMPT = b"Router> "
            self.TELNET_ENABLE_PROMPT = b"Router# "
            self.TELNET_LOGIN_PROMPT = b"Username: "
            self.TELNET_PASS_PROMPT = b"Password: "
            self.DEVICE_INFO = {
                "hostname": "Router",
                "model": "Cisco IOS Router",
                "version": "12.4(25b)",
                "hardware": "CISCO2811",
                "uptime": "3 days, 14 hours, 25 minutes",
                "interfaces": ["FastEthernet0/0", "FastEthernet0/1", "Serial0/0/0"]
            }

    async def start(self):
        """
        启动Telnet服务器
        """
        self._server = await asyncio.start_server(self._handle, host=self.host, port=self.port)
        addrs = ", ".join(str(sock.getsockname()) for sock in self._server.sockets)
        op_logger.info("Telnet监听于 %s", addrs)
        return asyncio.create_task(self._server.serve_forever())

    async def _handle(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
        """
        处理Telnet客户端连接，模拟网络设备CLI

        Args:
            reader (asyncio.StreamReader): 用于从连接中读取数据的流读取器
            writer (asyncio.StreamWriter): 用于向连接写入数据的流写入器
        """
        peer = writer.get_extra_info("peername") or ("unknown", 0)
        session_id = str(uuid.uuid4())
        start_ts = datetime.now(timezone.utc)
        raw_buf = bytearray()
        inputs = []
        login_text = ""
        pwd_text = ""
        login_attempts = 0

        op_logger.info("Telnet新连接 %s 会话=%s", peer, session_id)
        try:
            # 发送banner
            writer.write(self.TELNET_BANNER)
            await writer.drain()

            # 登录过程
            while login_attempts < 3 and (not login_text or not pwd_text):
                writer.write(self.TELNET_LOGIN_PROMPT)
                await writer.drain()
                login = await reader.readline()
                if not login:
                    await self._close_writer(writer)
                    return
                raw_buf.extend(login)
                login_text = login.decode(errors="ignore").strip()

                writer.write(self.TELNET_PASS_PROMPT)
                await writer.drain()
                password = await reader.readline()
                if not password:
                    await self._close_writer(writer)
                    return
                raw_buf.extend(password)
                pwd_text = password.decode(errors="ignore").strip()

                # 记录认证尝试到攻击日志
                attack_logger.log_auth_attempt(
                    source_ip=peer[0],
                    destination_port=self.port,
                    protocol=ProtocolType.TELNET,
                    username=login_text,
                    password=pwd_text
                )

                login_attempts += 1

            # 三次登录失败后断开连接
            if login_attempts >= 3 and (not login_text or not pwd_text):
                writer.write(b"\r\n% Login invalid\r\n")
                writer.write(b"% Login timed out after 3 minutes\r\n")
                await writer.drain()
                await self._close_writer(writer)
                return

            # 接受任何凭据（模拟弱密码策略）
            writer.write(b"\r\nUser Access Verification\r\n\r\n")
            await writer.drain()

            # 设置初始提示符和模式
            mode = "user"  # user or enable
            prompt = self.TELNET_PROMPT
            writer.write(prompt)
            await writer.drain()

            # 主命令循环
            while True:
                line = await reader.readline()
                if not line:
                    break
                raw_buf.extend(line)
                try:
                    original_text = line.decode(errors="ignore").rstrip("\r\n")
                except Exception:
                    original_text = "<无法解码>"
                inputs.append({"ts": datetime.now(timezone.utc).isoformat(), "input": original_text})
                lowered = original_text.lower()
                op_logger.debug("Telnet %s: %s", session_id, original_text)

                # 处理命令
                if lowered in ("quit", "exit"):
                    writer.write(b"\r\n% Connection closed by foreign host.\r\n")
                    await writer.drain()
                    break
                elif lowered == "logout":
                    writer.write(b"\r\n% Logout successful.\r\n")
                    await writer.drain()
                    break
                elif lowered == "enable":
                    # 进入特权模式
                    mode = "enable"
                    prompt = self.TELNET_ENABLE_PROMPT
                    writer.write(prompt)
                    await writer.drain()
                    continue
                elif lowered.startswith("disable"):
                    # 退出特权模式
                    mode = "user"
                    prompt = self.TELNET_PROMPT
                    writer.write(prompt)
                    await writer.drain()
                    continue
                elif lowered == "show version" or lowered == "sh ver":
                    await simulate_command_execution(0.5, 1.5)
                    writer.write(f"\r\nCisco IOS Software, {self.DEVICE_INFO['hardware']} Software (C2800NM-ADVENTERPRISEK9-M), Version {self.DEVICE_INFO['version']}, RELEASE SOFTWARE (fc1)\r\n".encode())
                    writer.write(b"Technical Support: http://www.cisco.com/techsupport\r\n")
                    writer.write(b"Copyright (c) 1986-2020 by Cisco Systems, Inc.\r\n")
                    writer.write(b"Compiled Thu 09-Jul-2020 15:34 by prod_rel_team\r\n\r\n")
                    writer.write(f"ROM: System Bootstrap, Version 12.4(13r)T11, RELEASE SOFTWARE (fc1)\r\n".encode())
                    writer.write(f"{self.DEVICE_INFO['hostname']} uptime is {self.DEVICE_INFO['uptime']}\r\n".encode())
                    writer.write(f"System returned to ROM by power-on\r\n".encode())
                    writer.write(f"System image file is \"flash:c2800nm-adventerprisek9-mz.124-25b.bin\"\r\n\r\n".encode())
                    writer.write(f"{self.DEVICE_INFO['hostname']}#".encode())
                    await writer.drain()
                    continue
                elif lowered == "show interfaces" or lowered == "sh int":
                    await simulate_command_execution(0.5, 1.5)
                    for interface in self.DEVICE_INFO["interfaces"]:
                        writer.write(f"\r\n{interface} is up, line protocol is up\r\n".encode())
                        writer.write(b"  Hardware is CNFDX, address is 0000.0000.0000 (bia 0000.0000.0000)\r\n")
                        writer.write(b"  Internet address is 192.168.1.1/24\r\n")
                        writer.write(b"  MTU 1500 bytes, BW 100000 Kbit/sec, DLY 100 usec,\r\n")
                        writer.write(b"     reliability 255/255, txload 1/255, rxload 1/255\r\n")
                        writer.write(b"  Encapsulation ARPA, loopback not set\r\n")
                        writer.write(b"  Keepalive set (10 sec)\r\n")
                    writer.write(b"\r\n")
                    writer.write(prompt)
                    await writer.drain()
                    continue
                elif lowered == "show ip interface brief" or lowered == "sh ip int br":
                    await simulate_command_execution(0.5, 1.0)
                    writer.write(b"\r\nInterface              IP-Address      OK? Method Status                Protocol\r\n")
                    for interface in self.DEVICE_INFO["interfaces"]:
                        writer.write(f"{interface:<22} 192.168.1.1     YES manual up                    up\r\n".encode())
                    writer.write(b"\r\n")
                    writer.write(prompt)
                    await writer.drain()
                    continue
                elif lowered == "show running-config" or lowered == "sh run":
                    await simulate_command_execution(1.0, 2.0)
                    writer.write(b"\r\nBuilding configuration...\r\n\r\n")
                    writer.write(b"Current configuration : 1234 bytes\r\n")
                    writer.write(b"!\r\n")
                    writer.write(b"version 12.4\r\n")
                    writer.write(b"no service timestamps log datetime msec\r\n")
                    writer.write(b"no service timestamps debug datetime msec\r\n")
                    writer.write(b"no service password-encryption\r\n")
                    writer.write(b"!\r\n")
                    writer.write(b"hostname Router\r\n")
                    writer.write(b"!\r\n")
                    writer.write(b"enable secret 5 $1$abc$defghijklmnopqrstuvwxyz\r\n")
                    writer.write(b"!\r\n")
                    writer.write(b"interface FastEthernet0/0\r\n")
                    writer.write(b" ip address 192.168.1.1 255.255.255.0\r\n")
                    writer.write(b" duplex auto\r\n")
                    writer.write(b" speed auto\r\n")
                    writer.write(b"!\r\n")
                    writer.write(b"line con 0\r\n")
                    writer.write(b" exec-timeout 0 0\r\n")
                    writer.write(b" logging synchronous\r\n")
                    writer.write(b"!\r\n")
                    writer.write(b"end\r\n\r\n")
                    writer.write(prompt)
                    await writer.drain()
                    continue
                elif lowered == "show startup-config" or lowered == "sh start":
                    await simulate_command_execution(0.5, 1.0)
                    writer.write(b"\r\nstartup-config is not set\r\n\r\n")
                    writer.write(prompt)
                    await writer.drain()
                    continue
                elif lowered == "show users":
                    await simulate_command_execution(0.2, 0.5)
                    writer.write(b"\r\n    Line       User       Host(s)              Idle       Location\r\n")
                    writer.write(b"   0 con 0                idle                 00:00:00   \r\n")
                    writer.write(b"*  1 vty 0                idle                 00:00:00   \r\n\r\n")
                    writer.write(b"  Interface    User               Mode         Idle     Peer Address\r\n\r\n")
                    writer.write(prompt)
                    await writer.drain()
                    continue
                elif lowered == "show processes":
                    await simulate_command_execution(0.5, 1.0)
                    writer.write(b"\r\nCPU utilization for five seconds: 1%/0%; one minute: 2%; five minutes: 1%\r\n")
                    writer.write(b" PID Runtime(ms)     Invoked      uSecs   5Sec   1Min   5Min TTY Process\r\n")
                    writer.write(b"   1          12        1605          7  0.00%  0.00%  0.00%   0 Chunk Manager\r\n")
                    writer.write(b"   2           4         542          7  0.00%  0.00%  0.00%   0 Load Meter\r\n")
                    writer.write(b"   3         100        1500         66  0.00%  0.00%  0.00%   0 DHCPD Timer\r\n")
                    writer.write(b"   4        2000       12000        166  0.00%  0.00%  0.00%   0 IP SNMP\r\n")
                    writer.write(b"   5         150        1200        125  0.00%  0.00%  0.00%   0 TCP Timer\r\n")
                    await writer.drain()
                elif lowered == "help":
                    writer.write(b"\r\nCisco CLI Help System\r\n\r\n")
                    writer.write(b"show version                - System hardware and software status\r\n")
                    writer.write(b"show interfaces             - Interface status and configuration\r\n")
                    writer.write(b"show ip interface brief     - Brief IP interface status\r\n")
                    writer.write(b"show running-config         - Current system configuration\r\n")
                    writer.write(b"show startup-config         - Startup configuration\r\n")
                    writer.write(b"show users                  - Display information about terminal lines\r\n")
                    writer.write(b"show processes              - CPU usage statistics\r\n")
                    writer.write(b"enable                      - Turn on privileged commands\r\n")
                    writer.write(b"disable                     - Turn off privileged commands\r\n")
                    writer.write(b"exit                        - Exit from the EXEC\r\n")
                    writer.write(b"logout                      - Exit from the EXEC\r\n")
                    await writer.drain()
                elif lowered.startswith("ping "):
                    await simulate_command_execution(1.0, 2.0)
                    target = original_text[5:].strip()
                    if target:
                        writer.write(f"\r\nType escape sequence to abort.\r\n".encode())
                        writer.write(f"Sending 5, 100-byte ICMP Echos to {target}, timeout is 2 seconds:\r\n".encode())
                        writer.write(b"!!!!!\r\n")
                        writer.write(b"Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms\r\n")

                        # 记录命令执行到攻击日志
                        attack_logger.log_command_execution(
                            source_ip=peer[0],
                            destination_port=self.port,
                            protocol=ProtocolType.TELNET,
                            command=original_text,
                            command_type="network"
                        )
                    else:
                        writer.write(b"\r\nUsage: ping <target>\r\n")
                    await writer.drain()
                elif lowered.startswith("traceroute "):
                    await simulate_command_execution(1.5, 3.0)
                    target = original_text[11:].strip()
                    if target:
                        writer.write(f"\r\nType escape sequence to abort.\r\n".encode())
                        writer.write(f"Tracing the route to {target}\r\n\r\n".encode())
                        writer.write(b"  1 192.168.1.1 0 msec 0 msec 4 msec\r\n")
                        writer.write(b"  2 10.0.0.1 4 msec 4 msec 4 msec\r\n")
                        writer.write(b"  3 8.8.8.8 8 msec *  8 msec\r\n")

                        # 记录命令执行到攻击日志
                        attack_logger.log_command_execution(
                            source_ip=peer[0],
                            destination_port=self.port,
                            protocol=ProtocolType.TELNET,
                            command=original_text,
                            command_type="network"
                        )
                    else:
                        writer.write(b"\r\nUsage: traceroute <target>\r\n")
                    await writer.drain()
                elif lowered == "reload":
                    writer.write(b"\r\nProceed with reload? [confirm]\r\n")
                    await writer.drain()
                    # 等待确认
                    confirm = await reader.readline()
                    raw_buf.extend(confirm)
                    confirm_text = confirm.decode(errors="ignore").rstrip("\r\n")
                    inputs.append({"ts": datetime.now(timezone.utc).isoformat(), "input": confirm_text})
                    if confirm_text.lower() in ["y", "yes", ""]:
                        writer.write(b"\r\nSystem configuration has been modified. Save? [yes/no]:\r\n")
                        await writer.drain()
                        save_confirm = await reader.readline()
                        raw_buf.extend(save_confirm)
                        save_text = save_confirm.decode(errors="ignore").rstrip("\r\n")
                        inputs.append({"ts": datetime.now(timezone.utc).isoformat(), "input": save_text})
                        writer.write(b"\r\nBuilding configuration...\r\n")
                        writer.write(b"[OK]\r\n")
                        writer.write(b"Reloading...\r\n")
                        await writer.drain()
                        await asyncio.sleep(2)
                        writer.write(b"\r\n% Connection closed by foreign host.\r\n")
                        await writer.drain()
                        break
                    else:
                        writer.write(b"\r\nReload cancelled.\r\n")
                        await writer.drain()
                else:
                    # 记录命令执行到攻击日志（未知命令）
                    if original_text.strip():
                        attack_logger.log_command_execution(
                            source_ip=peer[0],
                            destination_port=self.port,
                            protocol=ProtocolType.TELNET,
                            command=original_text,
                            command_type="unknown"
                        )

                    # 通用错误消息，模拟真实网络设备
                    if mode == "user" and lowered.startswith("show"):
                        writer.write(b"\r\n% Incomplete command.\r\n\r\n")
                    else:
                        writer.write(b"\r\n% Ambiguous command:  \"" + original_text.encode() + b"\"\r\n\r\n")
                    await writer.drain()

                writer.write(prompt)
                await writer.drain()

        except asyncio.CancelledError:
            op_logger.info("Telnet会话已取消 %s", session_id)
        except Exception as e:
            op_logger.exception("Telnet处理器错误 %s: %s", session_id, e)
        finally:
            entry = {
                "session_id": session_id,
                "start_time": start_ts.isoformat(),
                "end_time": datetime.now(timezone.utc).isoformat(),
                "duration_seconds": (datetime.now(timezone.utc) - start_ts).total_seconds(),
                "remote_ip": peer[0],
                "remote_port": peer[1],
                "login": login_text,
                "password": "<captured>" if pwd_text else None,
                "inputs": inputs,
                "raw_base64": base64.b64encode(bytes(raw_buf)).decode("ascii"),
            }
            await self.persist(entry)
            await self._close_writer(writer)
            op_logger.info("Telnet关闭会话 %s 来自 %s", session_id, peer)

    async def _close_writer(self, writer):
        """
        安全关闭writer
        """
        try:
            writer.close()
            await writer.wait_closed()
        except Exception:
            pass
